// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Diagnostics.ContractsLight;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BuildXL.Scheduler.Graph;
using Newtonsoft.Json;

namespace PipExecutionSimulator
{
    public static class Ex
    {
        public static string Format(this string s, string format, params object[] args)
        {
            object[] resultArgs = new object[args.Length + 1];
            resultArgs[0] = s;
            Array.Copy(args, 1, resultArgs, 0, args.Length);

            return string.Format(format, resultArgs);
        }

        public static string FormatWith(this string format, params object[] args)
        {
            return string.Format(format, args);
        }

        public static double ToSeconds(this ulong time)
        {
            return Math.Round(TimeSpan.FromTicks((long)time).TotalSeconds, 2);
        }

        public static double ToMinutes(this ulong time)
        {
            return Math.Round(TimeSpan.FromTicks((long)time).TotalMinutes, 3);
        }

        public static void CompareExchangeMax<T>(this ConcurrentNodeDictionary<T> map, NodeId node, T comparand) where T : IComparable<T>
        {
            if (comparand.IsGreaterThan(map[node]))
            {
                map[node] = comparand;
            }
        }

        public static void CompareExchangeMin<T>(this ConcurrentNodeDictionary<T> map, NodeId node, T comparand) where T : IComparable<T>
        {
            if (comparand.IsLessThan(map[node]))
            {
                map[node] = comparand;
            }
        }

        public static bool IsLessThan<T>(this T value, T other) where T : IComparable<T>
        {
            return value.CompareTo(other) < 0;
        }

        public static bool IsGreaterThan<T>(this T value, T other) where T : IComparable<T>
        {
            return value.CompareTo(other) > 0;
        }

        public static bool Max<T>(this T comparand, ref T value) where T : IComparable<T>
        {
            if (value.CompareTo(comparand) < 0)
            {
                value = comparand;
                return true;
            }

            return false;
        }

        public static bool Min<T>(this T comparand, ref T value) where T : IComparable<T>
        {
            if (value.CompareTo(comparand) > 0)
            {
                value = comparand;
                return true;
            }

            return false;
        }
    }
    /// <summary>
    /// Chains compares to perform comparison operations.
    /// </summary>
    public class ComparerBuilder<T> : Comparer<T>
    {
        private LinkedList<IComparer<T>> comparers = new LinkedList<IComparer<T>>();

        /// <summary>
        /// Modifies the comparer so that it next compares items using specified comparer on the key generated by the function.
        /// </summary>
        /// <typeparam name="TCompare">the type of key to compare</typeparam>
        /// <param name="keySelector">function for selecting the comparison key</param>
        /// <param name="comparer">the comparer for the key</param>
        /// <returns>This comparer updated with this comparison at the end of the comparison chain</returns>
        public ComparerBuilder<T> CompareByAfter<TCompare>(Func<T, TCompare> keySelector, IComparer<TCompare> comparer = null)
        {
            comparer = comparer ?? Comparer<TCompare>.Default;
            comparers.AddLast(new SelectorComparer<TCompare>(keySelector, comparer));
            return this;
        }

        /// <summary>
        /// Modifies the comparer so that it begins by comparing items using specified comparer on the key generated by the function.
        /// </summary>
        /// <typeparam name="TCompare">the type of key to compare</typeparam>
        /// <param name="keySelector">function for selecting the comparison key</param>
        /// <param name="comparer">the comparer for the key</param>
        /// <returns>This comparer updated with this comparison at the beginning of the comparison chain</returns>
        public ComparerBuilder<T> CompareByBefore<TCompare>(Func<T, TCompare> keySelector, IComparer<TCompare> comparer = null)
        {
            comparer = comparer ?? Comparer<TCompare>.Default;
            comparers.AddFirst(new SelectorComparer<TCompare>(keySelector, comparer));
            return this;
        }

        private class SelectorComparer<TCompare> : Comparer<T>
        {
            Func<T, TCompare> selector;
            IComparer<TCompare> comparer;

            public SelectorComparer(Func<T, TCompare> selector, IComparer<TCompare> comparer)
            {
                this.selector = selector;
                this.comparer = comparer;
            }

            public override int Compare(T x, T y)
            {
                return comparer.Compare(selector(x), selector(y));
            }
        }

        /// <summary>
        /// Compares the items using the list of stored comparers. The result is the first nonzero value returned
        /// by a comparer in the comparison chain. If none of the comparers returns, a non-zero value, the result is 
        /// zero.
        /// </summary>
        /// <param name="x">the first object to compare</param>
        /// <param name="y">the second object to compare</param>
        /// <returns>
        /// A signed integer that indicates the relative values of x and y, as shown
        /// in the following table.Value Meaning Less than zero, x is less than y.Zero, x
        /// equals y.Greater than zero, x is greater than y.</returns>
        public override int Compare(T x, T y)
        {
            if (object.ReferenceEquals(x, y))
            {
                return 0;
            }

            if (x == null)
            {
                return y == null ? 0 : -1;
            }
            if (y == null)
            {
                return 1;
            }

            var result = 0;
            foreach (var comparer in comparers)
            {
                result = comparer.Compare(x, y);
                if (result != 0)
                {
                    return result;
                }
            }

            return result;
        }
    }
}
